package dk.au.cs.bdsi.core.backend;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import dk.au.cs.bdsi.core.repositories.FIFO;

/**
 * An abstract listener, which encapsulates the behaviour and implementation
 * needed to poll a {@link FIFO} with regular intervals to check for new items,
 * and hands any such new items to a fresh {@link ListenerHandler} as provided by
 * a {@link ListenerHandlerFactory}.<br/>
 * <br/>
 * <b>Polling:</b> The {@link #SleepIntervalHigh} and {@link #SleepIntervalLow} 
 * poll delays are very important for respectively, lowering the usage when there
 * aren't any new items and maximizing throughput when there are.<br/>
 * The low value, defines the wait time for when there are no items in the fifo,
 * i.e. the time interval in-between polls to detect new items and thus affects
 * resource usage when there are no new items and the time it takes to detect new
 * items.<br/>
 * The high value, defines the wait time in-between processing new items (once it
 * has been detected that there are new items), and thus affects throughput (by
 * either limiting it or maximizing it).
 *
 * @param <E> Type of item the FIFO contains, handlers handle and factory returns.
 */
public abstract class AbstractListener<E> implements ServletContextListener, Runnable {
	/**
	 * This is the number of ms to wait in-between each poll, when the last poll
	 * was unsuccessful. I.e. the number of ms to wait to poll again, when there
	 * were no new items at the last poll.
	 */
	private final static int SleepIntervalLow = 5 * 1000;
	
	/**
	 * This is the number of ms to wait in-between each poll, when the last poll
	 * was successful. I.e. the number of ms to wait to poll again, when there
	 * were a new item at the last poll.
	 */
	private final static int SleepIntervalHigh = 100;
	
	private Thread backgroundThread = null;
	private ListenerHandlerFactory<E> factory;
	private FIFO<E> fifo;
	
	/**
	 * Construct a new abstract listener, with the given handler factory and fifo
	 * containing any new instances of {@link E} instances, which needs handling.
	 * 
	 * @param factory 
	 * @param fifo
	 */
	protected AbstractListener(ListenerHandlerFactory<E> factory, FIFO<E> fifo) {
		this.factory = factory;
		this.fifo = fifo;
	}
	
	@Override
	public final void contextDestroyed(ServletContextEvent arg0) {
		if (backgroundThread != null) {
			try {
				backgroundThread.interrupt();
			} catch (SecurityException e) {
				System.out.println("Warning: Unable to stop background thread: " + e.getMessage());
			}
		}
	}

	@Override
	public final void contextInitialized(ServletContextEvent arg0) {
		if (backgroundThread == null || !backgroundThread.isAlive()) {
			backgroundThread = new Thread(this);
			backgroundThread.start();
		}
	}
	
	@Override
	public final void run() {
		boolean high = false;
		service: while (true) {
			// Wait sleep interval
			try {
				Thread.sleep(high ? SleepIntervalHigh : SleepIntervalLow);
			} catch (InterruptedException e) {
				break service;
			}
			
			// Process any new items
			E item = fifo.dequeue();
			if (item != null) {
				high = true;
				factory.create().handle(item);
			} else {
				high = false;
			}
		}
	}
}
